/* 
 * File:   LZ77.cpp
 * Author: jorge
 * 
 * Created on April 11, 2013, 5:44 PM
 */

#include <string.h>
#include <string>
#include <sstream> 
#include <stdio.h>

#include "LZ77.h"
#include "logger.h"

const char* LZ77::compress(const char* file)
{
    int bytePosition = 0;
    int sizeFile = strlen(file);
    std::string window;
    std::string compressed;
    std::string content = file;

    while (bytePosition < sizeFile)
    {
        int foundPosition = window.find(content.at(bytePosition), 0);

        if (foundPosition == -1 || bytePosition == sizeFile - 1)
        {
            window += content.at(bytePosition);
            compressed += content.at(bytePosition);
            bytePosition++;
        } else
        {
            int pos;
            std::string longest = getLongest(content, bytePosition, window, pos);
            if (longest.size() < 3)
            {
                compressed += longest;
                window += longest;
                bytePosition += longest.size();
            } else
            {
                compressed += pair(pos, longest.size());
                window += longest;
                bytePosition += longest.size();
            }
        }
    }

    return compressed.c_str();
}

const char* LZ77::decompress(const char* file)
{
    int size = strlen(file);
    std::string buffer = file;
    std::string decompressed;

    for (int i = 0; i < size; i++)
    {
        if (buffer.at(i) != '(')
        {
            decompressed += buffer.at(i);
        } else
        {
            int position;
            int size;
            int aux = buffer.find(')', i) - i;
            std::string str = buffer.substr(i, 4);
            sscanf(str.data(), "(%d,%d)", &position,
                    &size);
            if (size == 0)
            {
                decompressed += buffer.at(i);
            } else
            {
                decompressed += decompressed.substr(position, size);
                i += aux;
            }
        }
    }

    return decompressed.data();
}

std::string LZ77::pair(int position, int size)
{
    std::stringstream buffer;
    buffer << "(";
    buffer << position;
    buffer << ",";
    buffer << size;
    buffer << ")";
    return buffer.str();
}

std::string LZ77::getLongest(std::string str, int pos, std::string subStr, int& index)
{
    int posFound = 0;
    std::string longest;

    while ((posFound = subStr.find(str.at(pos), posFound), posFound != -1))
    {
        std::string shortest = findLongest(str, pos, subStr, posFound);

        if (shortest.size() > longest.size())
        {
            longest = shortest;
            index = posFound;
        }
        posFound++;
    }

    return longest;
}

std::string LZ77::findLongest(std::string str, int pos, std::string subStr, int posSub)
{
    int size = 0;
    int aux = pos;

    while (pos < str.size() && posSub < subStr.size() && str.at(pos++) == subStr.at(posSub++))
    {
        size++;
    }

    return str.substr(aux, size);
}

